package com.paranoid.flashcard.dao;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;

import com.paranoid.flashcard.model.Card;
import com.paranoid.flashcard.model.Deck;
import com.paranoid.flashcard.model.Deck.DownloadStatus;
import com.paranoid.flashcard.model.Deck.Featured;
import com.paranoid.flashcard.model.QuizletCard;
import com.paranoid.flashcard.model.QuizletDeck;

public class FlashcardDao {
	
	public static FlashcardDao flashcardDao;
	
	private FlashcardDatabase mDbHelper;
	private SQLiteDatabase mWritableDb;
	private SQLiteDatabase mReadableDb;
	private Context mContext;
	private FileSystemCache mFsCache;
	
	public static FlashcardDao getInstance(Context context) {
		// TODO: change this, we don't want the context parameter here.
		if (flashcardDao == null) {
			flashcardDao = new FlashcardDao(context);
			flashcardDao.mContext = context;
		}
		return flashcardDao;
	}
	
	private FlashcardDao(Context context) {
		mDbHelper = new FlashcardDatabase(context);
		mWritableDb = mDbHelper.getWritableDatabase();
		mWritableDb.setLockingEnabled(false);
		mReadableDb = mDbHelper.getReadableDatabase();
		mContext = context;
		mFsCache = new FileSystemCache();
	}
	
	public void updateDeckStatus(String title, long id) {
		ContentValues content = new ContentValues();
		content.put(Deck.KEY_STATUS, DownloadStatus.DOWNLOADING.ordinal());
		mWritableDb.update(Deck.TABLE_NAME, content, Deck.KEY_ID + "=" + id, null);
	}
	
	public void insertDeck(long id, String description, int deletedFlag, DownloadStatus status, Featured featured) {
        ContentValues content = new ContentValues();
        content.put(Deck.KEY_ID, id);
        content.put(Deck.KEY_TITLE, description);
        content.put(Deck.KEY_STATUS, status.ordinal());
        content.put(Deck.KEY_FEATURED, featured.ordinal());
        mWritableDb.insert(Deck.TABLE_NAME, null, content);
	}
	
	public void updateDeck(long id, int status) {
		ContentValues content = new ContentValues();
		content.put(Deck.KEY_STATUS, status);
		mWritableDb.update(Deck.TABLE_NAME, content, Deck.KEY_ID+"="+id, null);
	}

	public Cursor getDownloadedDecks() {
	    String queryString = Deck.KEY_STATUS + "=" + DownloadStatus.DOWNLOADING.ordinal() + " OR " +
	               Deck.KEY_STATUS + "=" + DownloadStatus.LOCAL.ordinal();
        return mReadableDb.query(Deck.TABLE_NAME, null, queryString, null, null, null, null);
	}
	
    public Cursor getFeaturedDecks() {
        String queryString = Deck.KEY_STATUS + "=" + DownloadStatus.REMOTE.ordinal() +
            " AND " + Deck.KEY_FEATURED + "=" + Featured.YES.ordinal();
        return mReadableDb.query(Deck.TABLE_NAME, null, queryString, null, null, null, null);
    }

    public long insertCard(String sideA, String sideB, long deckId, byte[] imageA, byte[] imageB, int masteryLevel) {
		ContentValues args = new ContentValues();
		args.put(Card.KEY_TERM, sideA);
		args.put(Card.KEY_DEFINITION, sideB);
		args.put(Card.KEY_DECK, deckId);
//		args.put(Card.KEY_IMAGE_A, imageA);
//		args.put(Card.KEY_IMAGE_B, imageB);
		args.put(Card.KEY_MASTERY, masteryLevel);
		return mWritableDb.insert(Card.TABLE_NAME, null, args);
	}
	
	public Cursor getCardsForDeck(long deckId) {
		return mReadableDb.query(Card.TABLE_NAME, 
				new String[] {Card.KEY_ID, Card.KEY_TERM, Card.KEY_DEFINITION, Card.KEY_MASTERY}, 
				Card.KEY_DECK + "=?", new String[] {""+deckId}, 
				null, null, null);
	}
	
	public Deck getDeckById(long deckId) {
		Cursor deckCursor = mReadableDb.query(Deck.TABLE_NAME, new String[] {Deck.KEY_ID, Deck.KEY_TITLE}, Deck.KEY_ID + "=?", new String[] {""+deckId}, null, null, null);
		if (deckCursor.getCount() < 1)
			return null;
		deckCursor.moveToFirst();
		String deckDesc = deckCursor.getString(1);
		
		Cursor cardsCursor = getCardsForDeck(deckId);
		Deck result = new Deck(deckDesc, deckId, cardsCursor);
		return result;
	}
	
	public void saveMasteryLevels(Deck deck) {
		List<Card> cards = deck.getFlashCards();
		for (Card card : cards) {
			updateCardMasteryLevel(card.getId(), card.getMasteryLevel());
		}
	}
	
	public void updateCardMasteryLevel(long cardId, int masteryLevel) {
		ContentValues args = new ContentValues();
		args.put(Card.KEY_MASTERY, masteryLevel);
		mWritableDb.update(Card.TABLE_NAME, args, Card.KEY_ID + "=" + cardId, null);
	}
	
	public void markDeckDeleted(long deckId) {
		ContentValues args = new ContentValues();
		args.put(Deck.KEY_STATUS, DownloadStatus.DELETING.ordinal());
		mWritableDb.update(Deck.TABLE_NAME, args, Deck.KEY_ID + "=" + deckId, null);
	}
	
    public void markDeckRemote(long deckId) {
        ContentValues args = new ContentValues();
        args.put(Deck.KEY_STATUS, DownloadStatus.REMOTE.ordinal());
        mWritableDb.update(Deck.TABLE_NAME, args, Deck.KEY_ID + "=" + deckId, null);
    }

	public void deleteDeck(long deckId) {
	    mWritableDb.beginTransaction();
		mWritableDb.delete(Card.TABLE_NAME, Card.KEY_DECK + "=" + deckId, null);
		mWritableDb.setTransactionSuccessful();
        mWritableDb.endTransaction();
        mFsCache.deleteDeck(deckId);
	}
	
	public void insertQuizletDeck(QuizletDeck deck, List<QuizletCard> cards) {
	    mWritableDb.beginTransaction();
	    for (QuizletCard item : cards) {
			byte[] bytes = null;
			Bitmap b = getImage(item.img);
			if (b != null) {
				ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
				b.compress(Bitmap.CompressFormat.JPEG, 80, baos2); //bm is the bitmap object   
			    bytes = baos2.toByteArray();
			}
			long id = insertCard(item.def, item.term, deck.id, bytes, null, 0);
			mFsCache.saveCardImage(deck.id, id, bytes);            

		}
        mWritableDb.setTransactionSuccessful();
        mWritableDb.endTransaction();
        updateDeck(deck.id, DownloadStatus.LOCAL.ordinal());
	}
	
	private Bitmap getImage(String img) {
		try {
			img = img.replace("<img src=\"", "");
			String imgUrl = img.substring(0, img.indexOf("\""));
			Log.i("", "URL: " + imgUrl);
			URL url = new URL(imgUrl);
			return getRemoteImage(url);
		} catch (Exception ex) {return null;}
	}
	
	private Bitmap getRemoteImage(final URL aURL) {
	    try {
	        final URLConnection conn = aURL.openConnection();
	        conn.connect();
	        final BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
	        final Bitmap bm = BitmapFactory.decodeStream(bis);
	        bis.close();
	        return bm;
	    } catch (IOException e) {
	        Log.d("DEBUGTAG", "Oh noooz an error...");
	    }
	    return null;
	}
	
	public Bitmap getImage(long deckId, long cardId) {
		return mFsCache.getImage(deckId, cardId);
	}
	
}
